package com.tangsm.spring.boot.csv.utli;

import com.sun.istack.internal.NotNull;
import com.univocity.parsers.common.processor.RowListProcessor;
import com.univocity.parsers.csv.CsvFormat;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;
import com.univocity.parsers.csv.CsvWriter;
import com.univocity.parsers.csv.CsvWriterSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * CSV工具类
 *
 * @author tangyb
 * @date 2022/06/27
 */
public class CsvUtils {
    private static final Logger log = LoggerFactory.getLogger(CsvUtils.class);

    /**
     * 写入CSV文件
     *
     * @param heads    表头
     * @param rows     数据
     * @param outPath  文件输出目录
     * @param fileName 文件名称
     * @return 成功失败
     */
    public static boolean writeCsv(@NotNull String[] heads, @NotNull List<Object[]> rows, String outPath, @NotNull String fileName) {
        log.info("创建CSV文件开始，文件输出目录：{}，文件名称：{}，数据行数：{}", outPath, fileName, rows.size());
        try {
            if (fileName == null || "".equals(fileName)) {
                log.info("文件名称不能为空");
                return false;
            }

            // 文件输出目录不能为空
            if (outPath == null || "".equals(outPath)) {
                log.info("");
                return false;
            }

            // 给文件名添加后缀
            if (!fileName.toLowerCase(Locale.ROOT).endsWith(".csv")) {
                fileName = fileName.concat(".csv");
            }

            // 处理文件存放路径
            outPath = formatSeparator(outPath);
            // 测试此字符串是否以指定的后缀结尾
            if (!outPath.endsWith("/")) {
                outPath = outPath.concat("/");
            }

            // 创建文件
            File csvFile = new File(String.format("%s%s", outPath, fileName));
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();

            // CSV记录的输出资源
            Writer fileWriter = new FileWriter(csvFile);

            // CSV编写器(CsvWriter)使用的配置类
            CsvWriterSettings settings = new CsvWriterSettings();
            // 要解析/写入的文件格式
            CsvFormat format = settings.getFormat();
            // 定义字段分隔符。默认为“，”
            format.setDelimiter(',');
            // 定义是否应忽略空行（默认为 true）
            settings.setSkipEmptyLines(true);

            CsvWriter writer = new CsvWriter(fileWriter, settings);

            // 文件表头
            writer.writeHeaders(heads);

            // 遍历所有记录，写入它们并关闭输出
            writer.writeRowsAndClose(rows);
        } catch (Exception e) {
            log.error("创建CSV文件异常", e);
            return false;
        }
        log.info("创建CSV文件完成，文件输出路径：{}，文件名称：{}，数据行数：{}", outPath, fileName, rows.size());
        return true;
    }

    /**
     * 解析CSV文件
     *
     * @param csvPath CSV文件路径
     * @return 解析后的数据集合（不包含表头）
     */
    public static List<String[]> parseCsv(String csvPath) throws IOException {
        log.info("CSV文件路径：{}", csvPath);

        File file = new File(csvPath);

        // 文件不存在
        if (!file.exists()) {
            log.info("解析CSV文件失败，CSV文件不存在，CSV文件路径：{}", csvPath);
            return null;
        }

        // 解析CSV文件
        return parseCsv(Files.newInputStream(file.toPath()));
    }

    /**
     * 解析CSV文件
     *
     * @param bytes 二进制CSV文件
     * @return 解析后的数据集合（不包含表头）
     */
    public static List<String[]> parseCsv(byte[] bytes) {
        // 文件不存在
        if (bytes == null || bytes.length == 0) {
            log.info("解析CSV文件失败，请求参数为空");
            return null;
        }

        // 解析CSV文件
        return parseCsv(new ByteArrayInputStream(bytes));
    }

    /**
     * 解析CSV文件
     *
     * @param input 字节输入流
     * @return 解析后的数据集合（不包含表头）
     */
    public static List<String[]> parseCsv(InputStream input) {
        log.info("解析CSV文件开始");

        // 参数为空
        if (input == null) {
            log.info("解析CSV文件失败，参数为空。");
            return null;
        }

        // 创建一个配置选项，用来提供多种配置选项
        CsvParserSettings parserSettings = new CsvParserSettings();
        // 要解析的文件格式
        CsvFormat format = parserSettings.getFormat();
        // 定义字段分隔符。默认为“,”
        format.setDelimiter(',');
        // 打开解析器的自动检测功能，让它自动检测输入中包含的分隔符
        parserSettings.setLineSeparatorDetectionEnabled(true);

        // 创建RowListProcessor对象，用来把每个解析的行存储在列表中
        RowListProcessor rowListProcessor = new RowListProcessor();
        // 配置解析器
        parserSettings.setProcessor(rowListProcessor);
        // 待解析的CSV文件包含标题头，把第一个解析行看作文件中每个列的标题
        parserSettings.setHeaderExtractionEnabled(true);
        // 定义解析器是否应该自动检测行分隔符
        parserSettings.setLineSeparatorDetectionEnabled(true);

        //创建CsvParser对象，用于解析文件
        CsvParser parser = new CsvParser(parserSettings);
        // 设置要解析的文件
        parser.parse(input, StandardCharsets.UTF_8);

        //如果解析中包含标题，用于获取标题
        String[] headers = rowListProcessor.getHeaders();
        log.info("表头：{}", Arrays.toString(headers));

        //获取行值，并遍历打印
        List<String[]> rows = rowListProcessor.getRows();
        log.info("解析CSV文件完成，数据总行数：{}", CollectionUtils.isEmpty(rows) ? 0 : rows.size());

        return rows;
    }

    /**
     * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径
     *
     * @param filePath 文件路径
     * @return UNIX/Linux格式的文件路径
     */
    public static String formatSeparator(String filePath) {
        return filePath.replace("\\\\", "/");
    }
}
